package utils

import (
	"crypto/tls"
	"fmt"
	"net/smtp"
	"server/common"
	"strings"
)

type EmailConfig struct {
	Host     string
	Port     int
	Username string
	Password string
	From     string
}

// DefaultEmailConfig returns the email configuration using TLS port from application.yml
func DefaultEmailConfig() EmailConfig {
	config := common.GetEmailConfig()
	return EmailConfig{
		Host:     config.Host,
		Port:     config.PortTLS,
		Username: config.Username,
		Password: config.Password,
		From:     config.From,
	}
}

// SubmissionEmailConfig returns the email configuration using STARTTLS port from application.yml
func SubmissionEmailConfig() EmailConfig {
	config := common.GetEmailConfig()
	return EmailConfig{
		Host:     config.Host,
		Port:     config.PortSTARTTLS,
		Username: config.Username,
		Password: config.Password,
		From:     config.From,
	}
}

// SendEmail sends an email with the provided subject, body, and recipients using TLS (port 465)
func SendEmail(config EmailConfig, to []string, subject, body string) error {
	// Try the TLS method first
	err := sendEmailTLS(config, to, subject, body)
	if err != nil {
		// If TLS method fails, try using STARTTLS (port 587)
		starttlsConfig := config
		starttlsConfig.Port = common.GetEmailConfig().PortSTARTTLS
		return sendEmailSTARTTLS(starttlsConfig, to, subject, body)
	}
	return nil
}

// sendEmailTLS sends an email with TLS connection (port 465)
func sendEmailTLS(config EmailConfig, to []string, subject, body string) error {
	// Set up TLS configuration
	tlsConfig := &tls.Config{
		InsecureSkipVerify: false,
		ServerName:         config.Host,
	}

	// Connect to the SMTP server with TLS
	addr := fmt.Sprintf("%s:%d", config.Host, config.Port)
	conn, err := tls.Dial("tcp", addr, tlsConfig)
	if err != nil {
		return fmt.Errorf("failed to establish TLS connection: %v", err)
	}

	// Create a new SMTP client
	smtpClient, err := smtp.NewClient(conn, config.Host)
	if err != nil {
		return fmt.Errorf("failed to create SMTP client: %v", err)
	}
	defer smtpClient.Quit()

	// Authenticate
	auth := smtp.PlainAuth("", config.Username, config.Password, config.Host)
	if err := smtpClient.Auth(auth); err != nil {
		return fmt.Errorf("authentication failed: %v", err)
	}

	// Set the sender and recipients
	if err := smtpClient.Mail(config.From); err != nil {
		return fmt.Errorf("setting sender failed: %v", err)
	}
	for _, recipient := range to {
		if err := smtpClient.Rcpt(recipient); err != nil {
			return fmt.Errorf("setting recipient failed: %v", err)
		}
	}

	// Send the email body
	writer, err := smtpClient.Data()
	if err != nil {
		return fmt.Errorf("getting data writer failed: %v", err)
	}

	// Compose the message
	msg := fmt.Sprintf("To: %s\r\n"+
		"From: %s\r\n"+
		"Subject: %s\r\n"+
		"Content-Type: text/plain; charset=UTF-8\r\n"+
		"\r\n"+
		"%s", strings.Join(to, ","), config.From, subject, body)

	if _, err := writer.Write([]byte(msg)); err != nil {
		return fmt.Errorf("writing message failed: %v", err)
	}

	if err := writer.Close(); err != nil {
		return fmt.Errorf("closing data writer failed: %v", err)
	}

	return nil
}

// sendEmailSTARTTLS sends an email using STARTTLS (port 587)
func sendEmailSTARTTLS(config EmailConfig, to []string, subject, body string) error {
	// Authentication
	auth := smtp.PlainAuth("", config.Username, config.Password, config.Host)

	// Compose the message
	msg := []byte(fmt.Sprintf("To: %s\r\n"+
		"From: %s\r\n"+
		"Subject: %s\r\n"+
		"Content-Type: text/plain; charset=UTF-8\r\n"+
		"\r\n"+
		"%s", strings.Join(to, ","), config.From, subject, body))

	// SMTP server address
	addr := fmt.Sprintf("%s:%d", config.Host, config.Port)

	// Send the email
	return smtp.SendMail(addr, auth, config.From, to, msg)
}
